 /*
  * Team Java
  * This is the Scheduler along with it's dependency ADT's
  * Author(s): Michael Satran Jr, Michael Pogson, Stephen Hunter
  * Date: 5/31/2012
  */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "pcb.h"
#include "scheduler.h"
#include "io.h"
#include "memory.h"
#include "display.h"

int flag;
int locked;

 //Pass in queue's head and tail.
 void initQueue(Node** head, Node** tail)
 {
	*head = *tail = 0;
 }

 void addProcess(Process* proc)
 {
	process_list[list_index].proc = proc;
	process_list[list_index].state = state_ready;
	process_list[list_index].id = list_index;
	process_list[list_index].pc = 0;

	enque(&rq_head, &rq_tail, &process_list[list_index]);

	list_index++;
 }

 //Places a process which is already in the PCB list into the ready queue
 void enqueProcess(int id)
 {
	enque(&rq_head, &rq_tail, &process_list[id]);
 }

 void enqueProcesses(int order[32], int n)
 {
	int i = 0;
	for (; i < n; i++)
	{
		enque(&rq_head, &rq_tail, &process_list[order[i]]);
	}
 }

 void initScheduler()
 {
	initQueue(&rq_head, &rq_tail);
	initQueue(&bq_head, &bq_tail);
	memstart();
 }

 void *startScheduler()
 {
	pthread_t threads[2];
	pthread_attr_t attr;

	pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	pthread_create(&threads[0], &attr, start, NULL);
	while (1)
	{
		//print();
		runNextProcess();
	}
 }

 //Returns 0 if queue is empty
 PCB* deque(Node** head, Node** tail)
 {
	Node* old = *head;
	PCB* data = 0;

	if (old != 0)
	{
		*head = (*head)->prev;
		old->next = 0;
		old->prev = 0;

		data = old->data;

		old = 0;
	} else
		*tail = *head;

	return data;
 }

 int enque(Node** head, Node** tail, PCB* process_pointer)
 {
	Node *new_node = (Node*)malloc(sizeof(Node));
	new_node->data = process_pointer;

	new_node->prev = 0;
	new_node->next = *tail;

	if (*tail != 0)
	{
		(*tail)->prev = new_node;
	}

	*tail = new_node;

	if (*head == 0)
	{
		*head = *tail;
	}

	return 0;
 }

 //Does nothing (idle) if there is no next process
 void *runNextProcess()
 {
	PCB* p = deque(&rq_head, &rq_tail);

	if (p != 0)
	{
		Process* p2 = p->proc;

		pthread_attr_t attr;
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

		pthread_create(&threads[p->id], &attr, process_func, (void *)p);
		pthread_join(threads[p->id], NULL);
	}

 }

 void do_request(PCB *pcb, Request r)
 {
	pthread_t threads[5];
	pthread_attr_t attr;

	pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	
	
	switch(r)
	{
		case io_system_call:
			printf("Process %d made a IO call\n",pcb->id);
			pthread_create(&threads[0], &attr, systemCall, NULL);
			pthread_create(&threads[0], &attr, handleInterupt, NULL);
			break;
		case mutex_acquire_system_call:
		printf("Process %d made a mutex acquire call\n",pcb->id);
			pthread_create(&threads[0], &attr, mutexAcquire, NULL);
			break;
		case mutex_give_system_call:
		printf("Process %d made a mutex give call\n",pcb->id);
			pthread_create(&threads[0], &attr, mutexGive, NULL);
			break;
		case mem_write_system_call:
		printf("Process %d made a mem write call\n",pcb->id);
			pthread_create(&threads[0], &attr, memWrite, NULL);
			break;
		case mem_read_system_call:
		printf("Process %d made a mem read call\n",pcb->id);
			pthread_create(&threads[1], &attr, memRead, NULL);
			break;
	}
		pthread_join(threads[0], NULL);
 }

 void *process_func(void* arg)
 {
	PCB* pcb = (PCB*)arg;
	Process *p = (pcb->proc);
	int req = 1;

	int steps = p->steps_n;
	int requests = p->requests_n;

	//Set the PC
	int i = pcb->pc;
	for (i = 0; i < steps; i++)
	{
		if ((requests - req) > 0 && i % 100 == 0)
		{
			do_request(pcb, req);
			req++;
		}
		pcb->pc = i;

	}
	pcb->pc = 0;
	enque(&rq_head, &rq_tail, pcb);
 }

 void interuptHandler(int io) {
	pthread_t thread1;
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	
	while(flag!=0) {
		
	}

	switch(io)
	{
		case 1:
		flag = 1;
		pthread_create(&thread1, &attr, handleInterupt, NULL);
		break;
		case 2:
		flag = 1;
		pthread_create(&thread1, &attr, handleInterupt, NULL);
		break;
		case 3:
		flag = 1;
		pthread_create(&thread1, &attr, handleInterupt, NULL);
		break;
	}
	pthread_join(thread1, NULL);
	flag = 0;
	switch(io)
	{
		case 1:
		pthread_create(&thread1, &attr, diskInterupt,(void*) 1000);
		break;
		case 2:
		pthread_create(&thread1, &attr, videoInterupt, (void *)1000);
		break;
		case 3:
		pthread_create(&thread1, &attr, keyInterupt, NULL);
		break;
	}
 }